package com.cloudcross.ssp.base.web;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import javax.servlet.ServletContext;

import org.apache.commons.collections4.ListUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.cloudcross.ssp.base.web.result.AjaxResult;
import com.cloudcross.ssp.common.Config;
import com.cloudcross.ssp.common.consts.HttpContentType;
import com.cloudcross.ssp.common.utils.DateUtil;
import com.cloudcross.ssp.common.utils.DateUtil.DateFmts;
import com.cloudcross.ssp.model.Account;

/**
 * 支持上传controller。
 * 
 * @author wuqiang.du
 */
public abstract class UploadController extends SimpleController {
    /** 匿名 */
    private static final String ANONYMOUS = "anon";

    private Logger LOG = LogManager.getLogger(getClass());

    @Autowired
    private Config config;

    /**
     * 异步（ajax）处理上传文件请求。
     * 
     * @param file
     *            上传文件。
     * @return 返回上传成功（失败）信息。
     */
    @RequestMapping(value = "upload/{resourceType}", method = { RequestMethod.POST, RequestMethod.GET })
    public final @ResponseBody AjaxResult upload(@PathVariable String resourceType, @RequestParam MultipartFile file) {
        AjaxResult result = new AjaxResult();
        if (validate(resourceType, file)) {
            result.setStatus(OK);
            result.addData("url", handleUpload(resourceType, file));
        } else {
            result.setMsg("上传图片、flash格式或者大小不对");
            result.setStatus(ERROR);
        }
        return result;
    }

    /**
     * 验证上传文件是否合法，如果不合法那么会抛出异常
     * 
     * @param file
     *            用户上传的文件封装类
     */
    public final boolean validate(String resourceType, MultipartFile file) {
        Assert.notEmpty(getUploadAllowedContentTypes(resourceType), "The content types allowed to be uploaded must contain one at least!");
        Assert.notNull(file, "The multipart file is null!");

        if (getUploadMaxSize(resourceType) != -1 && file.getSize() > getUploadMaxSize(resourceType)) {
            LOG.error("The file uploaded is out of max file size!");
            return false;
        }
        if (!getUploadAllowedContentTypes(resourceType).contains(file.getContentType())) {
            LOG.error("The content type '" + file.getContentType() + "' is not a valid content type !");
            return false;
        }
        return true;
    }

    /**
     * 子类可以重写。
     * 
     * @return 设置可接受的文件content-type数组（默认支持）
     */
    public List<String> getUploadAllowedContentTypes(String resourceType) {
        return ListUtils.union(HttpContentType.IMG_JPG, HttpContentType.IMG_PNG);
    }

    /**
     * 子类可以重写。
     * 
     * @return 设置文件上传大小上限（默认1M，单位字节）
     */
    public long getUploadMaxSize(String resourceType) {
        return 1 * 1024 * 1024;
    }

    /**
     * 如果有特殊处理，子类可以复写。
     * 
     * @param resourceType
     *            上传资源类型（video\image）
     * @param file
     *            上传文件。
     * @return 网络访问路径（url）
     */
    protected String handleUpload(String resourceType, MultipartFile file) {
        Account loginUser = getLoginUser();

        // 计算相对路径名
        String localFilePath = new StringBuilder(config.getResourceUploadDir()).append(File.separator)
                .append(loginUser == null ? ANONYMOUS : loginUser.getId()).append(File.separator).append(resourceType).append(File.separator)
                .append(DateUtil.getDate(DateFmts.YYYYMMDD)).append(File.separator).append(System.currentTimeMillis()).append(".")
                .append(FilenameUtils.getExtension(file.getOriginalFilename())).toString();

        // 计算在容器中实际路径名
        String accessUrl = localFilePath;
        ServletContext servletContext = getServletContext();
        if (servletContext == null) {
            LOG.debug("单体测试情况，servletContext不存在。");
        } else {
            accessUrl = servletContext.getContextPath().concat(File.separator).concat(localFilePath);
            localFilePath = servletContext.getRealPath(localFilePath);
        }

        // 保存文件
        File localFile = new File(localFilePath);
        copyFile(file, localFile);
        LOG.info("上传保存后的文件：" + localFile.getAbsolutePath());

        return accessUrl;
    }

    /**
     * 拷贝文件。
     */
    protected final void copyFile(MultipartFile remoteFile, File localFile) {
        // 所属文件夹不存在，创建所有。
        File localDir = localFile.getParentFile();
        if (!localDir.exists()) {
            LOG.info("创建路径：" + localDir.getAbsolutePath());
            localDir.mkdirs();
        }

        // 将文件保存到本地
        InputStream is = null;
        FileOutputStream os = null;
        try {
            is = remoteFile.getInputStream();
            os = new FileOutputStream(localFile);
            IOUtils.copy(is, os);
        } catch (IOException e) {
            LOG.error("将资源存到本地时失败。", e);
        } finally {
            IOUtils.closeQuietly(os);
            IOUtils.closeQuietly(is);
        }
    }

}
